package com.king.security.interceptor;

import com.king.security.base.Result;
import com.king.security.entity.MyUser;
import com.king.security.enums.RedisKeyEnum;
import com.king.security.enums.ResultEnums;
import com.king.security.exception.BusinessException;
import com.king.security.target.MyAuthentication;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @description:
 * @author: HHJ
 * @create: 2019-10-15 14:01
 **/
@Slf4j
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ApplicationContext applicationContext;
    /**
     * 是否启用UserLoginToken注解,默认启用true hhj20200604
     */
    @Value("${vipthink.core.interceptor.annotation.enableUserLoginToken:true}")
    private Boolean enableUserLoginToken;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            //检查Header数据
            String httpMethod = request.getMethod();
            //权限验证
            String authorization = request.getHeader("Authorization");
            // 如果不是映射到方法直接通过
            if (!(handler instanceof HandlerMethod)) {
                return true;
            }
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();

            //检查有没有需要用户权限的注解
            if (!enableUserLoginToken) {
                log.warn("用户权限的注解已禁用!enableUserLoginToken:{}", enableUserLoginToken);
            }
            if (enableUserLoginToken && method.isAnnotationPresent(MyAuthentication.class)) {
                MyAuthentication MyAuthentication = method.getAnnotation(MyAuthentication.class);
                if (!MyAuthentication.required()) {
                    return true;
                }
                //验证用户权限
                return this.handleUserLoginToken(request, httpMethod);
            }

            return true;
        } catch (BusinessException e) {
            log.warn("用户认证失败!---详细信息:[{}]---RequestURI:[{}]", e.getMsg(), request.getRequestURI());
            throwPostInterceptor(request, response, handler, e.getMsg());
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 整个请求处理完毕回调方法
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        //清除线程上下文
        //前端用户
        LoginUserVoContext.removeLoginUserVo();
    }


    /**
     * 验证用户权限
     *
     * @param request
     * @param httpMethod
     * @return
     * @throws IOException
     */
    private boolean handleUserLoginToken(HttpServletRequest request, String httpMethod) throws IOException {

        String token = "";
        if (null != request.getHeader("token")) {
            token = request.getHeader("token");
        }
        //判断token是否为空
        if (StringUtils.isEmpty(token)) {
            throw new BusinessException(ResultEnums.NOT_LOGIN_ERROR.getMessage());
        }

        Object user = redisTemplate.opsForValue().get(RedisKeyEnum.LOGIN_USER + token);
        if (user == null) {
            throw new BusinessException(ResultEnums.NOT_LOGIN_ERROR.getMessage());
        }

        MyUser loginUserVo = new MyUser();
        loginUserVo.setUserName(user.toString());
        //添加到上下文
        LoginUserVoContext.setLoginUserVo(loginUserVo);
        return true;
    }


    /**
     * 拦截后处理返回信息(处理throw new BusinessException)
     */
    protected void throwPostInterceptor(HttpServletRequest request, HttpServletResponse response, Object handler, String resultEnums) throws Exception {
        // 返回json
        Object returnValue = Result.buildError(resultEnums);
        // 如果被拦截，返回信息
        HandlerMethod handlerMethod = new HandlerMethod(((HandlerMethod) handler).getBean(), ((HandlerMethod) handler).getMethod());
        MethodParameter returnValueType = handlerMethod.getReturnValueType(returnValue);
        applicationContext.getBean(RequestMappingHandlerAdapter.class).getReturnValueHandlers();
        RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = getRequestResponseBodyMethodProcessor();
        requestResponseBodyMethodProcessor.handleReturnValue(returnValue, returnValueType, new ModelAndViewContainer(), new ServletWebRequest(request, response));
        // end
    }

    protected RequestResponseBodyMethodProcessor getRequestResponseBodyMethodProcessor() {
        RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
        for (HandlerMethodReturnValueHandler value : Objects.requireNonNull(requestMappingHandlerAdapter.getReturnValueHandlers())) {
            if (value instanceof RequestResponseBodyMethodProcessor) {
                return (RequestResponseBodyMethodProcessor) value;
            }
        }
        // SpringMVC的环境下一定不会走到这里
        throw new UnsupportedOperationException("cannot find RequestResponseBodyMethodProcessor from RequestMappingHandlerAdapter by Spring Context.");
    }
}
